﻿using CrawlerFramework.ConfigEntity;
using CrawlerFramework.Models;
using CrawlerFramework.Repository;
using MediaBot.Entities;
using MediaBot.Library;
using RakutenProxy.ConfigEntity;
using RakutenProxy.DataConverter.Helper;
using RakutenProxy.Model.Rakuten;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml;

namespace RakutenProxy.PageProcessor
{
    internal class RakutenProductsPageProcessor : CrawlerFramework.PageProcessor.PageProcessor
    {
        readonly Guid ProductDetailLinkConfigGuid = Guid.Parse("39656082-6746-45DE-A580-733773E0CE7D");

        public RakutenProductsPageProcessor(LinkConfig linkConfig, Link link)
            : base(linkConfig, link)
        {
        }

        public override CrawlerFramework.Models.CrawlStatus Execute()
        {
            Logger.Info("Start with url: " + Link.Url);

            if (!ShouldExecute())
                return CrawlStatus.Paused;

            var productRepo = new Repository.BaseRakutenRepository<Product>();
            var linkRepo = new LinkRepository();
            var manufacturerRepo = new Repository.BaseRakutenRepository<Manufacturer>();
            var jobRepo = new ScheduledJobRepository();

            try
            {
                Link.BeginExecution();


                var entityConfigPath = LinkConfig.BuildEntityConfigPath();
                var xmlSerializer = new XmlSerializer<RakutenProductsConfig>();
                var productsConfig = xmlSerializer.Deserialize(entityConfigPath);

                var namespaces = Utils.GetDefaultNamespaces();

                var url = Link.Url;
                var page = 0;


                while (!string.IsNullOrEmpty(url) && page <= productsConfig.PagesLimit)
                {
                    page += 1;

                    var xmlWebPage = new XmlWebPage(new Uri(url), namespaces);
                    xmlWebPage.Download();

                    var nsManager = xmlWebPage.XmlNamespaceManager;

                    var productNodes = xmlWebPage.SelectNodes(productsConfig.ProductNode.Context);

                    foreach (XmlNode productNode in productNodes)
                    {
                        var productName = productsConfig.ProductName.GetString(productNode, nsManager);
                        var productUrl = productsConfig.ProductUrl.GetString(productNode, nsManager);

                        var product = productRepo.Single(p => p.Url == productUrl);
                        if (product == null)
                        {
                            product = new Product { ProductName = productName, Url = productUrl };
                        }

                        var manufacturerName = MatchHelper.GetString(productsConfig.ManufacturerName, productNode, nsManager);
                        var manufacturerUrl = MatchHelper.GetString(productsConfig.ManufacturerUrl, productNode, nsManager);

                        var manufacturer = manufacturerRepo.Single(m => m.ManufacturerName == manufacturerUrl);
                        if (manufacturer == null && manufacturerName != null)
                        {
                            manufacturer = new Manufacturer { ManufacturerName = manufacturerName, ManufacturerUrl = manufacturerUrl };
                            manufacturerRepo.Add(manufacturer);
                            manufacturerRepo.Save();

                            product.ManufacturerId = manufacturer.ManufacturerId;
                        }


                        var priceStr = MatchHelper.GetString(productsConfig.Price, productNode, nsManager);
                        var thumbnailImageUrl = MatchHelper.GetString(productsConfig.ThumbnailImage, productNode, nsManager);
                        var shortDescription = MatchHelper.GetString(productsConfig.ShortDescription, productNode, nsManager);

                        decimal price = Utils.GetDecimalFromString(priceStr);

                        var thumbnail = string.IsNullOrEmpty(thumbnailImageUrl) ? null : ImageDownloader.Download(thumbnailImageUrl);

                        product.ProductName = productName;
                        product.Url = productUrl;
                        product.Price = price;
                        product.ThumbnailImage = thumbnail;
                        product.ShortDescription = shortDescription;
                        product.CurrencyId = 1;
                        product.CategoryId = Link.RelatedEntityId.HasValue ? Link.RelatedEntityId.Value : 0;

                        productRepo.InsertOrUpdate(product, product.ProductId);
                        productRepo.Save();

                        // Port product to nopcommerce database
                        ConvertData(product.ProductId, DataConverter.RakutenDataConverterFactory.GetDataConverter(typeof(Product)));

                        var link = new Link { Url = product.Url, RelatedEntityName = typeof(Product).Name, RelatedEntityId = product.ProductId };
                        linkRepo.InsertOrUpdate(link);
                        linkRepo.Save();

                        var detailJob = jobRepo.Single(j => j.LinkId == link.LinkId);
                        if (detailJob == null)
                        {
                            detailJob = new ScheduledJob { LinkId = link.LinkId, JobGuid = Guid.NewGuid(), LinkConfigGuid = ProductDetailLinkConfigGuid, Hours = 0, IsEnabled = true, JobDescription = "Crawl detail of a product using the specified linkId", CrawlStatus = CrawlStatus.Initial };
                            jobRepo.Add(detailJob);
                            jobRepo.Save();
                        }                        

                        CrawlerEngine.ExecuteScheduledJob(detailJob.JobGuid);
                    }

                    var nextPageUrl = xmlWebPage.SelectNodes(productsConfig.NextPage.Context);
                    if (nextPageUrl.Count > 0)
                    {
                        url = nextPageUrl[0].Value;
                    }
                    else
                        url = null;

                    Link.Page = page;
                }
            }
            catch (Exception e)
            {
                var method = System.Reflection.MethodBase.GetCurrentMethod();
                var exceptionOnMethod = string.Format("{0}.{1}", method.ReflectedType, method.Name);

                var errorMessage = Utils.BuildErrorMessage(method, LinkConfig.Guid, Link.LinkId);
                Logger.Error(errorMessage, e);

                Link.EndExecution(CrawlStatus.CompletedWithError);
                return CrawlStatus.CompletedWithError;
            }
            finally
            {
                productRepo.Dispose();
                linkRepo.Dispose();
                manufacturerRepo.Dispose();
                jobRepo.Dispose();

                // When done
                if (DoneEvent != null) DoneEvent.Set();

                Logger.Info("Finish link: " + Link.Url);
            }

            Link.EndExecution(CrawlStatus.Completed);

            return CrawlStatus.Completed;
        }
    }
}